﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using Comun;

namespace ProgramacionRedes {
    public class Conexion {
        internal static int ReceiveBufferSize = 256;

        private Usuario usuario = null;
        public Usuario Usuario {
            get { return usuario; }
            set { usuario = value; }
        }
        Servidor servidor = null;
        TcpClient cliente = null;
        NetworkStream stream;

        public event Log_Changed evLogChanged = null;

        bool procesar = true;

        public void Iniciar(TcpClient incliente, Servidor servidor) {
            this.servidor = servidor;
            this.cliente = incliente;
            this.stream = incliente.GetStream();

            Thread ctThread = new Thread(Recibir);
            ctThread.Start();
        }

        private void Recibir() {
            while (procesar) {
                try {
                    Comando comando = Comando.ObtenerComando(cliente.GetStream());
                    procesarComando(comando);
                } catch (Exception exc) {
                    Log("ERROR: " + exc.Message);
                    Console.WriteLine(" >> " + exc.ToString());
                }
            }
            cliente.Close();
            Log("Conexión cerrada");
        }

        private void responderOK(enuOrden orden) {
            Comando cmd = new Comando();
            cmd.Encabezado = enuHeader.RES;
            cmd.Orden = orden;
            cmd.Parametros.Add("OK");
            cmd.Enviar(stream);
        }

        private void responderERROR(enuOrden orden, string descripcion) {
            Comando cmd = new Comando();
            cmd.Encabezado = enuHeader.RES;
            cmd.Orden = orden;
            cmd.Parametros.Add("ERROR");
            cmd.Parametros.Add(descripcion);
            cmd.Enviar(stream);
        }

        private bool procesarComando(Comando comando) {
            Comando cmd_respuesta;
            bool res = false;
            try {
                if (comando.Encabezado == enuHeader.REQ) {
                    if (comando.Orden == enuOrden.Sesion) {
                        if (usuario == null) {
                            if (comando.Parametros.Count >= 2) {
                                Usuario usuario_encontrado = servidor.ObtnerUsuarioNoConectado(comando.Parametros[0], comando.Parametros[1]);
                                this.usuario = usuario_encontrado;
                                servidor.AgregarConexion(this);
                                responderOK(enuOrden.Sesion);
                                res = true;
                            } else {
                                throw new Exception("Cantidad de parámetros del comando insuficiente");
                            }
                        } else {
                            throw new Exception("Usuario ya conectado");
                        }
                    } else {
                        if (usuario != null) {
                            switch (comando.Orden) {
                                case enuOrden.Cerrar:
                                    Log("Comando cerrar conexión");
                                    servidor.QuitarConexion(this);
                                    procesar = false;
                                    responderOK(comando.Orden);
                                    res = true;
                                    break;
                                case enuOrden.AmigosBloqueados:
                                    cmd_respuesta = new Comando();
                                    cmd_respuesta.Encabezado = enuHeader.RES;
                                    cmd_respuesta.Orden = comando.Orden;
                                    cmd_respuesta.Parametros.Add("OK");
                                    foreach (Usuario u in Usuario.AmigosBloqueados) {
                                        cmd_respuesta.Parametros.Add(u.Nombre);
                                    }
                                    cmd_respuesta.Enviar(cliente.GetStream());
                                    res = true;
                                    break;
                                case enuOrden.AmigosNoBloqueados:
                                    cmd_respuesta = new Comando();
                                    cmd_respuesta.Encabezado = enuHeader.RES;
                                    cmd_respuesta.Orden = comando.Orden;
                                    cmd_respuesta.Parametros.Add("OK");
                                    foreach (Usuario u in Usuario.AmigosNoBloqueados) {
                                        cmd_respuesta.Parametros.Add(u.Nombre);
                                    }
                                    cmd_respuesta.Enviar(cliente.GetStream());
                                    res = true;
                                    break;
                                case enuOrden.SolicitarAmistad:
                                    if (comando.Parametros.Count > 0) {
                                        Usuario nuevo_amigo = servidor.ObtnerUsuario(comando.Parametros[0]);
                                        usuario.AgregarAmigo(nuevo_amigo);
                                        responderOK(comando.Orden);
                                        res = true;
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.AgregarEstado:
                                    if (comando.Parametros.Count > 0) {
                                        if (comando.Parametros[0] == "") {
                                            throw new Exception("Estado vacío");
                                        } else {
                                            usuario.AgregarEstado(comando.Parametros[0]);
                                            responderOK(comando.Orden);
                                            res = true;
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.QuitarEstado:
                                    if (comando.Parametros.Count > 0) {
                                        if (comando.Parametros[0] == "") {
                                            throw new Exception("Estado vacío");
                                        } else {
                                            int id_estado;
                                            try {
                                                id_estado = int.Parse(comando.Parametros[0]);
                                            } catch {
                                                throw new Exception("El id del estado no es un entero");
                                            }
                                            usuario.QuitarEstado(id_estado);
                                            responderOK(comando.Orden);
                                            res = true;
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.Ping:
                                    responderOK(comando.Orden);
                                    break;
                                case enuOrden.ComentarEstado:
                                    if (comando.Parametros.Count > 2) {
                                        if (comando.Parametros[0] == "") {
                                            throw new Exception("Usuario vacío");
                                        } else if (comando.Parametros[1] == "") {
                                            throw new Exception("Estado vacío");
                                        } else if (comando.Parametros[2] == "") {
                                            throw new Exception("Comentario vacío");
                                        } else {
                                            Usuario usuario_comentado = servidor.ObtnerUsuario(comando.Parametros[0]);
                                            Usuario.ComentarEstado(usuario_comentado, int.Parse(comando.Parametros[1]), comando.Parametros[1]);
                                            responderOK(comando.Orden);
                                            res = true;
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.EstadosAmigo:
                                    if (comando.Parametros.Count > 0) {
                                        if (comando.Parametros[0] == "") {
                                            throw new Exception("Usuario vacío");
                                        } else {
                                            List<Estado> estados;
                                            if (comando.Parametros[0] == Usuario.Nombre) {
                                                estados = Usuario.ObtenerEstados(Usuario);
                                            } else {
                                                Usuario usu = servidor.ObtnerUsuario(comando.Parametros[0]);
                                                estados = Usuario.ObtenerEstados(usu);
                                            }
                                            cmd_respuesta = new Comando();
                                            cmd_respuesta.Encabezado = enuHeader.RES;
                                            cmd_respuesta.Orden = comando.Orden;
                                            cmd_respuesta.Parametros.Add("OK");
                                            foreach (Estado estado in estados) {
                                                cmd_respuesta.Parametros.Add(estado.Id.ToString());
                                            }
                                            cmd_respuesta.Enviar(cliente.GetStream());
                                            res = true;
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.UsuarioQueComenta:
                                    if (comando.Parametros.Count > 2) {
                                        if (comando.Parametros[0] == "") throw new Exception("Usuario vacío");
                                        if (comando.Parametros[1] == "") throw new Exception("Estado vacío");
                                        if (comando.Parametros[2] == "") throw new Exception("Comentario vacío");
                                        string nombre_usuario_comentado = comando.Parametros[0];
                                        int id_estado_comentado;
                                        try {
                                            id_estado_comentado = int.Parse(comando.Parametros[1]);
                                        } catch {
                                            throw new Exception("El id del estado no es un entero");
                                        }
                                        int id_comentario;
                                        try {
                                            id_comentario = int.Parse(comando.Parametros[2]);
                                        } catch {
                                            throw new Exception("El id del comentario no es un entero");
                                        }
                                        Usuario usuario_comentado = servidor.ObtnerUsuario(nombre_usuario_comentado);
                                        if (Usuario.Amigos.Contains(usuario_comentado)) {
                                            bool enc = false;
                                            Estado estado_comentado = null;
                                            foreach (Estado estado in usuario_comentado.Estados) {
                                                if (estado.Id == id_estado_comentado) {
                                                    enc = true;
                                                    estado_comentado = estado;
                                                    break;
                                                }
                                            }
                                            if (enc) {
                                                enc = false;
                                                Comentario comentario_comentado = null;
                                                foreach (Comentario comentario in estado_comentado.Comentarios) {
                                                    if (comentario.Id == id_comentario) {
                                                        enc = true;
                                                        comentario_comentado = comentario;
                                                        break;
                                                    }
                                                }
                                                if (enc) {
                                                    cmd_respuesta = new Comando();
                                                    cmd_respuesta.Encabezado = enuHeader.RES;
                                                    cmd_respuesta.Orden = comando.Orden;
                                                    cmd_respuesta.Parametros.Add("OK");
                                                    cmd_respuesta.Parametros.Add(comentario_comentado.Usuario.Nombre);
                                                    cmd_respuesta.Enviar(cliente.GetStream());
                                                    res = true;
                                                } else {
                                                    throw new Exception("No existe comentario con ese id");
                                                }
                                            } else {
                                                throw new Exception("No existe estado con ese id");
                                            }
                                        } else {
                                            throw new Exception("No se puede obtener los estados de un usuario del cual no se es amigo");
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.BloquearAmigo:
                                    if (comando.Parametros.Count > 0) {
                                        Usuario amigo = servidor.ObtnerUsuario(comando.Parametros[0]);
                                        usuario.BloquearAmigo(amigo);
                                        responderOK(comando.Orden);
                                        res = true;
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.DesbloquearAmigo:
                                    if (comando.Parametros.Count > 0) {
                                        Usuario amigo = servidor.ObtnerUsuario(comando.Parametros[0]);
                                        usuario.DesbloquearAmigo(amigo);
                                        responderOK(comando.Orden);
                                        res = true;
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.TextoEstado:
                                    if (comando.Parametros.Count > 1) {
                                        if (comando.Parametros[0] == "") {
                                            throw new Exception("Usuario vacío");
                                        } else if (comando.Parametros[1] == "") {
                                            throw new Exception("Id Estado vacío");
                                        } else {
                                            string nombre_usuario = comando.Parametros[0];
                                            int id_estado;
                                            try {
                                                id_estado = int.Parse(comando.Parametros[1]);
                                            } catch {
                                                throw new Exception("El id del estado no es un entero");
                                            }
                                            List<Estado> estados;
                                            if (comando.Parametros[0] == Usuario.Nombre) {
                                                estados = Usuario.ObtenerEstados(Usuario);
                                            } else {
                                                Usuario usu = servidor.ObtnerUsuario(nombre_usuario);
                                                estados = Usuario.ObtenerEstados(usu);
                                            }
                                            bool encontrado = false;
                                            string texto = "";
                                            foreach (Estado estado in estados) {
                                                if (estado.Id == id_estado) {
                                                    encontrado = true;
                                                    texto = estado.Texto;
                                                }
                                            }
                                            if (encontrado) {
                                                cmd_respuesta = new Comando();
                                                cmd_respuesta.Encabezado = enuHeader.RES;
                                                cmd_respuesta.Orden = comando.Orden;
                                                cmd_respuesta.Parametros.Add("OK");
                                                cmd_respuesta.Parametros.Add(texto);
                                                cmd_respuesta.Enviar(cliente.GetStream());
                                                res = true;
                                            } else {
                                                throw new Exception("No existe estado con ese id");
                                            }
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.TextoComentario:
                                    if (comando.Parametros.Count > 2) {
                                        if (comando.Parametros[0] == "") {
                                            throw new Exception("Usuario vacío");
                                        } else if (comando.Parametros[1] == "") {
                                            throw new Exception("Id Estado vacío");
                                        } else if (comando.Parametros[2] == "") {
                                            throw new Exception("Id Comentario vacío");
                                        } else {
                                            string nombre_usuario = comando.Parametros[0];
                                            int id_estado;
                                            try {
                                                id_estado = int.Parse(comando.Parametros[1]);
                                            } catch {
                                                throw new Exception("El id del estado no es un entero");
                                            }
                                            int id_comentario;
                                            try {
                                                id_comentario = int.Parse(comando.Parametros[2]);
                                            } catch {
                                                throw new Exception("El id del comentario no es un entero");
                                            }
                                            List<Estado> estados;
                                            if (comando.Parametros[0] == Usuario.Nombre) {
                                                estados = Usuario.ObtenerEstados(Usuario);
                                            } else {
                                                Usuario usu = servidor.ObtnerUsuario(nombre_usuario);
                                                estados = Usuario.ObtenerEstados(usu);
                                            }
                                            bool encontrado = false;
                                            Estado estado_enc = null;
                                            foreach (Estado estado in estados) {
                                                if (estado.Id == id_estado) {
                                                    encontrado = true;
                                                    estado_enc = estado;
                                                }
                                            }
                                            if (encontrado) {
                                                encontrado = false;
                                                string texto = "";
                                                foreach (Comentario comentario in estado_enc.Comentarios) {
                                                    if (comentario.Id == id_comentario) {
                                                        encontrado = true;
                                                        texto = comentario.Texto;
                                                    }
                                                }
                                                if (encontrado) {
                                                    cmd_respuesta = new Comando();
                                                    cmd_respuesta.Encabezado = enuHeader.RES;
                                                    cmd_respuesta.Orden = comando.Orden;
                                                    cmd_respuesta.Parametros.Add("OK");
                                                    cmd_respuesta.Parametros.Add(texto);
                                                    cmd_respuesta.Enviar(cliente.GetStream());
                                                    res = true;
                                                } else {
                                                    throw new Exception("No existe comentario con ese id");
                                                }
                                            } else {
                                                throw new Exception("No existe estado con ese id");
                                            }
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }
                                    break;
                                case enuOrden.ComentariosEstado:
                                    if (comando.Parametros.Count > 1) {
                                        if (comando.Parametros[0] == "") {
                                            throw new Exception("Usuario vacío");
                                        } else if (comando.Parametros[1] == "") {
                                            throw new Exception("Id Estado vacío");
                                        } else {
                                            string nombre_usuario = comando.Parametros[0];
                                            int id_estado;
                                            try {
                                                id_estado = int.Parse(comando.Parametros[1]);
                                            } catch {
                                                throw new Exception("El id del estado no es un entero");
                                            }
                                            List<Estado> estados;
                                            if (comando.Parametros[0] == Usuario.Nombre) {
                                                estados = Usuario.ObtenerEstados(Usuario);
                                            } else {
                                                Usuario usu = servidor.ObtnerUsuario(nombre_usuario);
                                                estados = Usuario.ObtenerEstados(usu);
                                            }
                                            bool encontrado = false;
                                            Estado estado_enc = null;
                                            foreach (Estado estado in estados) {
                                                if (estado.Id == id_estado) {
                                                    encontrado = true;
                                                    estado_enc = estado;
                                                }
                                            }
                                            if (encontrado) {
                                                cmd_respuesta = new Comando();
                                                cmd_respuesta.Encabezado = enuHeader.RES;
                                                cmd_respuesta.Orden = comando.Orden;
                                                cmd_respuesta.Parametros.Add("OK");
                                                foreach (Comentario c in estado_enc.Comentarios) {
                                                    cmd_respuesta.Parametros.Add(c.Id.ToString());
                                                }
                                                cmd_respuesta.Enviar(cliente.GetStream());
                                                res = true;
                                            } else {
                                                throw new Exception("No existe estado con ese id");
                                            }
                                        }
                                    } else {
                                        throw new Exception("Comando incorrecto, faltan parámetros");
                                    }

                                    break;
                                default:
                                    throw new Exception("Comando no implementado");
                            }
                        } else {
                            throw new Exception("Usuario no logeado");
                        }
                    }
                } else {
                    throw new Exception("Comando incorrecto, encabezado debe ser REQ");
                }
            } catch (Exception exc) {
                responderERROR(comando.Orden, exc.Message);
                Log("ERROR_PROCESAR_COMANDO: " + exc.Message);
            }
            return res;
        }

        public void Terminar() {
            procesar = false;
        }

        public override bool Equals(object obj) {
            if (usuario == null || obj == null) {
                return false;
            } else {
                if (obj.GetType() != this.GetType()) {
                    return false;
                } else {
                    return this.usuario == ((Conexion)obj).usuario;
                }
            }
        }

        //private 
        private void Log(string texto) {
            if (evLogChanged != null) {
                evLogChanged(this, new EventArgs_Log(texto));
            }
        }
    }
}
